/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2019-2019.
 * Description: coredump on syscall exit or exit_group
 * Author: nixiaoming
 * Create: 2019-03-02
 */

#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/sched/mm.h>
#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/rtos_mm_types.h>
#include <linux/rtos/coredump_on_exit.h>
#include <linux/uaccess.h>
#include "internal.h"
#include "proc_rtos.h"

static ssize_t proc_coredump_on_exit_read(struct file *file, char __user *buf,
		size_t count, loff_t *ppos)
{
	ssize_t ret = 0;
	struct mm_struct *mm = NULL;
	struct rtos_mm_struct *rtos_mm = NULL;
	char buffer[PROC_NUMBUF] = {0};
	struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode);
	size_t len;

	if (!task)
		return -ESRCH;

	mm = get_task_mm(task);
	if (!mm) {
		ret = -ESRCH;
		goto fail;
	}
	rtos_mm = mm_to_rtos_mm(mm);
	/*
	 * buffer is a local array, size is greater than 3(now is 13).
	 * "%d\n" prints 0,1,2 members, the data length is limited to 3,
	 * so it is impossible to overflow here.
	 * avoid dead code, do not judge the return value len.
	 */
	len = snprintf(buffer, sizeof(buffer), "%d\n",
			rtos_mm->dump_on_exit);
	mmput(mm);
	ret = simple_read_from_buffer(buf, count, ppos, buffer, len);
fail:
	put_task_struct(task);
	return ret;
}

static ssize_t proc_coredump_on_exit_write(struct file *file,
		const char __user *buf, size_t count, loff_t *ppos)
{
	struct task_struct *task = NULL;
	struct mm_struct *mm = NULL;
	struct rtos_mm_struct *rtos_mm = NULL;
	int val = 0;
	ssize_t ret;

	if (count > 2) { /* only accept one char */
		pr_info("[task:%s %d][func: %s] count > 2\n",
			current->comm, current->pid, __func__);
		return -EINVAL;
	}

	if (kstrtoint_from_user(buf, count, 0, &val)) {
		pr_info("[task:%s %d][func: %s] get_user fail\n",
			current->comm, current->pid, __func__);
		return -EFAULT;
	}

	if (val != NEED_DUMP_ON_EXIT && val != NO_DUMP_ON_EXIT) {
		pr_info("[task:%s %d][func: %s] only accept %d or %d\n",
			current->comm, current->pid, __func__, NEED_DUMP_ON_EXIT, NO_DUMP_ON_EXIT);
		return -EINVAL;
	}

	task = get_proc_task(file->f_path.dentry->d_inode);
	if (!task)
		return -ESRCH;

	mm = get_task_mm(task);
	if (!mm) {
		ret = -ESRCH;
		goto fail;
	}
	rtos_mm = mm_to_rtos_mm(mm);
	rtos_mm->dump_on_exit = val;
	mmput(mm);
	ret = count;
fail:
	put_task_struct(task);
	return ret;
}

const struct file_operations proc_coredump_on_exit_operations = {
	.read   = proc_coredump_on_exit_read,
	.write  = proc_coredump_on_exit_write,
};

